﻿using System;
using System.Windows;
using System.Reflection;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace AccessControl.Wpf.Actions
{
	class ExecuteNamedCommandAction : TriggerAction<FrameworkElement>
	{
		#region Fields

		private static readonly Type s_ThisType = typeof(ExecuteNamedCommandAction);

		#endregion

		#region Dependency Properties

		public static readonly DependencyProperty CommandNameProperty = DependencyProperty.Register(
			"CommandName",
			typeof(string),
			s_ThisType,
			new PropertyMetadata(null));

		public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
			"CommandParameter",
			typeof(object),
			s_ThisType,
			new PropertyMetadata(null));

		public static readonly DependencyProperty RelativeVisualObjectProperty = DependencyProperty.Register(
			"RelativeVisualObject",
			typeof(DependencyObject),
			s_ThisType,
			new PropertyMetadata(null));

		#endregion

		#region Public Properties

		public string CommandName
		{
			get { return (string)GetValue(CommandNameProperty); }
			set { SetValue(CommandNameProperty, value); }
		}

		public object CommandParameter
		{
			get { return GetValue(CommandParameterProperty); }
			set { SetValue(CommandParameterProperty, value); }
		}

		public DependencyObject RelativeVisualObject
		{
			get { return (DependencyObject)GetValue(RelativeVisualObjectProperty); }
			set { SetValue(RelativeVisualObjectProperty, value); }
		}

		#endregion

		protected override void Invoke(object parameter)
		{
			ICommand command = FindCommand();
			if (command != null)
			{
				if (CommandParameter == null)
					command.Execute(parameter);
				else
					command.Execute(CommandParameter);
			}
		}

		private ICommand FindCommand()
		{
			ICommand command = null;
			object dataContext = null;
			PropertyInfo commandProperty = null;
			DependencyObject currentElement = null;

			if (RelativeVisualObject != null)
				currentElement = RelativeVisualObject;
			else
				currentElement = AssociatedObject;

			while (currentElement != null && command == null)
			{
				/* First find the data context */
				dataContext = currentElement.GetValue(FrameworkElement.DataContextProperty);
				if (dataContext != null)
				{
					/* Check for the command name property */
					commandProperty = dataContext.GetType().GetProperty(CommandName);
					/* Validate */
					if (commandProperty != null)
					{
						command = commandProperty.GetValue(dataContext, null) as ICommand;
					}
				}

				/* And get the next one in the visual tree */
				currentElement = VisualTreeHelper.GetParent(currentElement);
			}

			return command;
		}
	}
}
